﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using AdAggregator.Parser;
using AdAggregator.Util;
using AdAggregator.Website;
using CsQuery;
using RealEstateAggregator.Models.SearchData;
using log4net;

namespace RealEstateAggregator.Models.Parsers
{
    public class OtoDomPlParser : BaseParser<OtoDomPlSearchData, MyAdvertisement>
    {
        private readonly ILog log = LogManager.GetLogger(typeof(OtoDomPlParser));

        private const string uriTemplate = "http://otodom.pl/index.php?mod=listing&source=context&objSearchQuery.OfferType={0}&objSearchQuery.ObjectName={1}&objSearchQuery.Province.ID={2}&objSearchQuery.CityName={3}&objSearchQuery.PriceFrom={4}&objSearchQuery.PriceTo={5}&objSearchQuery.AreaFrom={6}&objSearchQuery.AreaTo={7}&objSearchQuery.FlatFloorFrom={8}&objSearchQuery.FlatFloorTo={9}&objSearchQuery.Description={10}&resultsPerPage=25&currentPage={11}&objSearchQuery.QuarterName={12}&objSearchQuery.StreetName={13}&objSearchQuery.PriceM2From={14}&objSearchQuery.PriceM2To={15}&objSearchQuery.BuildingYearFrom={16}&objSearchQuery.BuildingYearTo={17}&objSearchQuery.FlatFloorFrom={18}&objSearchQuery.FlatFloorTo={19}&objSearchQuery.MarketType={20}&objSearchQuery.OnlyWithPhoto={21}&objSearchQuery.Orderby={22}";

        protected override String ComposeUri()
        {

            var uri = String.Format(uriTemplate, SearchData.AdvertisementType, SearchData.EstateType, SearchData.Voivodship,
                SearchData.City, SearchData.PriceMin, SearchData.PriceMax, SearchData.AreaMin, SearchData.AreaMax,
                SearchData.RoomCountFrom, SearchData.RoomCountTo, SearchData.Keywords, SearchData.Page, SearchData.District,
                SearchData.Street, SearchData.PricePerMeterMin, SearchData.PricePerMeterMax, SearchData.YearBuiltMin,
                SearchData.YearBuiltMax, SearchData.FloorMin, SearchData.FloorMax, SearchData.MarketType,
                SearchData.OnlyWithPhoto, SearchData.OrderBy);

            return uri;
        }

        public override string Name
        {
            get { return "OtoDomPl"; }
        }

        protected override IParseResult<MyAdvertisement> ParseResponse(HttpWebResponse response, IAdvertisementSource<MyAdvertisement> website)
        {
            var list = new List<MyAdvertisement>();
            var dom = CQ.Create(response.GetResponseStream());
            var availableString = dom[".od-found-offers span strong"].Text().RemoveWhitespaces();
            var parsedAvailable = availableString.ToNullableLong();
            var allAvailable = parsedAvailable.HasValue ? parsedAvailable.Value : 0;
            var ads = dom["article.od-listing_item"];
            foreach (var el in ads)
            {
                var myAd = new MyAdvertisement();
                try
                {
                    var ad = CQ.Create(el);
                    var title = ad[".od-listing_item-title a"];
                    var img = ad[".od-listing_item-img img"];

                    String link;
                    if (title.Attr("href").StartsWith("http"))
                    {
                        continue;
                    }

                    link = new StringBuilder("http://otodom.pl").Append(title.Attr("href")).ToString();
                    myAd.Link = link;
                    ParseFullAd(link, myAd);
                    var numbers = ad["od-listing_item-numbers"];
                    var price = numbers[".od-listing_item-price"];
                    CQ area;
                    if (SearchData.EstateType == "Room")
                    {
                        area = numbers[".od-listing_item-price ~ span:not(.od-listing_item-good-price)"];
                        myAd.PricePerMeter = ComputePricePerMeter(price.Text(), area.Text());
                    }
                    else
                    {
                        area = numbers[".od-listing_item-price ~ strong"];
                        var pricePerMeter = area.Next("span");

                        myAd.PricePerMeter = pricePerMeter.Length > 0
                                                 ? pricePerMeter.Text().RemoveLastAtTheEnd("zł / m2").ToNullableDecimal()
                                                 : null;
                    }
                    myAd.Area = area.Length > 0
                                    ? area.Text().RemoveLastAtTheEnd("m2").ToNullableDecimal()
                                    : null;
                    myAd.Price = price.Text().RemoveLastAtTheEnd("zł").RemoveWhitespaces().ToNullableDecimal();
                    myAd.Title = title.Text();
                    myAd.Photo = img.HasAttr("data-src") ? img.Attr("data-src") : null;
                    myAd.AdSource = Name + ".png";
                    myAd.Source = website;
                    list.Add(myAd);
                }
                catch (Exception ex)
                {
                    log.Debug(String.Format("{0}: exception while parsing [{1}]", GetType().Name, myAd.Link), ex);
                }
            }
            return new ParseResult<MyAdvertisement> { Results = list, AllAvailable = allAvailable};
        }

        private decimal? ComputePricePerMeter(string price, string area)
        {
            decimal priceNum, areaNum;
            if (decimal.TryParse(price.RemoveFirstAtTheEnd(" "), out priceNum))
            {
                if (decimal.TryParse(area.RemoveFirstAtTheEnd(" "), out areaNum))
                {
                    return Math.Round(priceNum / areaNum, 2);
                }
            }
            return null;
        }

        private void ParseFullAd(string link, MyAdvertisement myAd)
        {
            var innerRequest = WebRequest.Create(link) as HttpWebRequest;
            var innerResponse = innerRequest.GetResponse() as HttpWebResponse;
            var innerDom = CQ.Create(innerResponse.GetResponseStream());
            var table = innerDom[".od-offer-box"];
            var locEntry = table["dt:contains(lokalizacja:)"];
            var floorEntry = table["dt:contains(piętro:)"];
            var yearEntry = table["dt:contains(rok budowy:)"];
            var modified = table["p:contains(Data aktualizacji:)"].Find("strong").Text();
            if (String.IsNullOrEmpty(modified)) modified = table["p:contains(Data dodania:)"].Find("strong").Text();

            if (SearchData.EstateType == "House" || SearchData.EstateType == "Flat")
            {
                int? roomCnt = FindRoomCount(table["dd"].First().Text());
                myAd.RoomCount = roomCnt;
            }
            else
            {
                myAd.RoomCount = null;
            }
            ParseLocation(locEntry.Next("dd").Text(), myAd);
            myAd.Floor = floorEntry.Length > 0 ? FindFloor(floorEntry.Next("dd").Text()) : null;
            myAd.YearBuilt = yearEntry.Length > 0 ? yearEntry.Next("dd").Text().ToNullableInt() : null;
            myAd.Modified = DateTime.ParseExact(modified, "d.MM.yyyy", null);
        }

        private void ParseLocation(string locString, MyAdvertisement myAd)
        {
            var entries = locString.Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var entry in entries)
            {
                if (entry.StartsWith("województwo: "))
                {
                    myAd.Voivodship = entry.RemoveFirstAtTheBeginning("województwo: ").Trim();
                }
                else if (entry.StartsWith("miejscowość: "))
                {
                    myAd.City = entry.RemoveFirstAtTheBeginning("miejscowość: ").Trim();
                }
                else if (entry.StartsWith("dzielnica: "))
                {
                    myAd.District = entry.RemoveFirstAtTheBeginning("dzielnica: ").Trim();
                }
                else if (entry.StartsWith("ulica: "))
                {
                    myAd.Street = entry.RemoveFirstAtTheBeginning("ulica: ").Trim();
                }
            }
        }

        private int? FindFloor(string text)
        {
            var str = text.RemoveFirstAtTheEnd(" ");
            if (str.Trim().ToLower() == "parter") return 0;
            return str.ToNullableInt();
        }

        private int? FindRoomCount(string text)
        {
            var strgs = text.Split(',');
            if (strgs.Length > 0)
            {
                var line = strgs[0];
                if (line.RemoveWhitespaces().StartsWith("kawalerka"))
                    return 1;
                var rooms = line.RemoveLastAtTheEnd("-");
                return rooms.ToNullableInt();
            }
            return null;
        }
    }
}